# Check requisite packages are installed.
packages <- c(
  "plotly", 
  "dplyr"
)
for (pkg in packages) {
  library(pkg, character.only = TRUE)
}
package 㤼㸱plotly㤼㸲 was built under R version 4.0.5package 㤼㸱ggplot2㤼㸲 was built under R version 4.0.3package 㤼㸱dplyr㤼㸲 was built under R version 4.0.4

Tabs

Load

Pulling code almost directly from LM1996-NumPoolComScaling-Results-2021-05.Rmd.

dirViking <- c(
  file.path(
    getwd(), "LCAB_LawMorton1996-NumericalPoolCommunityScaling9"
  ),
  file.path(
    getwd(), "LCAB_LawMorton1996-NumericalPoolCommunityScaling10"
  )
)
dirVikingResults <- file.path(
  dirViking, c("save-2021-08-03") # Latter not 100% yet.
)
resultFormat <- paste0(
  "run-", 
  "%d", # Combination Number, or CombnNum.
  "-", 
  "%s", # Run Seed.
  ".RDS"
)
source(
  file.path(getwd(), 
            "LawMorton1996-NumericalPoolCommunityScaling-Settings9.R")
)

paramFrame <- with(list(
  b = rep(basal, times = length(consumer)),
  c = rep(consumer, each = length(basal)),
  s1 = seedsPrep[1:(length(basal) * length(consumer))],
  s2 = seedsPrep[
    (length(basal) * length(consumer) + 1):(
      2 * length(basal) * length(consumer))
  ],
  sR = seedsRun
), {
  temp <- data.frame(
    CombnNum = 0,
    Basals = b,
    Consumers = c,
    SeedPool = s1,
    SeedMat = s2,
    SeedRuns = "",
    SeedRunsNum = 0,
    EndStates = I(rep(list(""), length(b))),
    EndStatesNum = 0,
    EndStateSizes = I(rep(list(""), length(b))),
    EndStateSizesNum = NA,
    EndStateAssembly = I(rep(list(""), length(b))),
    EndStateAbundance = I(rep(list(""), length(b))),
    Dataset = "Viking9",
    DatasetID = 1,
    stringsAsFactors = FALSE
  )
  for (i in 1:nrow(temp)) {
    seeds <- sR[((i - 1) * runs + 1) : (i * runs)]
    temp$SeedRuns[i] <- toString(seeds) # CSV
    temp$SeedRunsNum[i] <- length(seeds)
  }
  temp$CombnNum <- 1:nrow(temp)
  temp
})
# Note: n + 2 end states. Failure to finish, failure to obtain state, and state.
for (i in 1:nrow(paramFrame)) {
  resultsList <- list(
    "No Run" = 0,
    "No State" = 0
  )
  resultsSize <- list(
    "0" = 0
  )
  resultsAssembly <- list(
    "No Run" = data.frame(),
    "No State" = data.frame()
  )
  resultsAbund <- list(
    "No Run" = "",
    "No State" = ""
  )
  seeds <- unlist(strsplit(paramFrame$SeedRuns[i], ', '))
  for (seed in seeds) {
    fileName <- file.path(
      dirVikingResults[paramFrame$DatasetID[i]],
      sprintf(resultFormat, paramFrame$CombnNum[i], seed)
    )
    
    if (file.exists(fileName)) {
      temp <- load(fileName)
      temp <- eval(parse(text = temp)) # Get objects.
      
      if (is.list(temp) && "Result" %in% names(temp)) {
        
        if (is.data.frame(temp$Result))
          community <- temp$Result$Community[[nrow(temp$Result)]]
        else 
          community <- temp$Result
        
        if (length(community) > 100) {
          print(paste(i, length(community), seed))
        }
        
        size <- toString(length(community))
        
        if (community[1] != "") 
          abund <- toString(temp$Abund[community + 1])
        else 
          abund <- ""
        
        community <- toString(community)
        
        if (community == "") {
          resultsList$`No State` <- resultsList$`No State` + 1
          resultsSize$`0` <- resultsSize$`0` + 1
          
        } else if (community %in% names(resultsList)) {
          resultsList[[community]] <- resultsList[[community]] + 1
          resultsSize[[size]] <- resultsSize[[size]] + 1
          
        } else {
          resultsList[[community]] <- 1
          resultsAssembly[[community]] <- temp
          resultsAbund[[community]] <- abund
          
          if (size %in% resultsSize) {
            resultsSize[[size]] <- resultsSize[[size]] + 1
          } else {
            resultsSize[[size]] <- 1
          }
        }
      } else {
        resultsList$`No State` <- resultsList$`No State` + 1
        resultsSize$`0` <- resultsSize$`0` + 1
      }
    } else {
      resultsList$`No Run` <- resultsList$`No Run` + 1
      resultsSize$`0` <- resultsSize$`0` + 1
    }
  }
  
  paramFrame$EndStates[[i]] <- resultsList
  paramFrame$EndStatesNum[i] <- length(resultsList) - 2 # ! No State, No Run
  paramFrame$EndStateSizes[[i]] <- resultsSize
  paramFrame$EndStateSizesNum[i] <- length(resultsSize) - 1 # ! 0
  paramFrame$EndStateAssembly[[i]] <- resultsAssembly
  paramFrame$EndStateAbundance[[i]] <- resultsAbund
}
[1] "10 119 95557008.0550388"
[1] "10 116 69699811.3999143"
source(
  file.path(getwd(), 
            "LawMorton1996-NumericalPoolCommunityScaling-Settings10.R")
)

oldNrow <- nrow(paramFrame)

paramFrame <- rbind(paramFrame, with(list(
  b = rep(basal, times = length(consumer)),
  c = rep(consumer, each = length(basal)),
  s1 = seedsPrep[1:(length(basal) * length(consumer))],
  s2 = seedsPrep[
    (length(basal) * length(consumer) + 1):(
      2 * length(basal) * length(consumer))
  ],
  sR = seedsRun
), {
  temp <- data.frame(
    CombnNum = 0,
    Basals = b,
    Consumers = c,
    SeedPool = s1,
    SeedMat = s2,
    SeedRuns = "",
    SeedRunsNum = 0,
    EndStates = I(rep(list(""), length(b))),
    EndStatesNum = 0,
    EndStateSizes = I(rep(list(""), length(b))),
    EndStateSizesNum = NA,
    EndStateAssembly = I(rep(list(""), length(b))),
    EndStateAbundance = I(rep(list(""), length(b))),
    Dataset = "Viking10",
    DatasetID = 2,
    stringsAsFactors = FALSE
  )
  for (i in 1:nrow(temp)) {
    seeds <- sR[((i - 1) * runs + 1) : (i * runs)]
    temp$SeedRuns[i] <- toString(seeds) # CSV
    temp$SeedRunsNum[i] <- length(seeds)
  }
  temp$CombnNum <- 1:nrow(temp)
  temp
})
)
# Note: n + 2 end states. Failure to finish, failure to obtain state, and state.
# Modified from above, but with the abundance recorded.
for (i in (oldNrow + 1):nrow(paramFrame)) {
  resultsList <- list(
    "No Run" = 0,
    "No State" = 0
  )
  resultsSize <- list(
    "0" = 0
  )
  resultsAssembly <- list(
    "No Run" = data.frame(),
    "No State" = data.frame()
  )
  resultsAbund <- list(
    "No Run" = "",
    "No State" = ""
  )
  seeds <- unlist(strsplit(paramFrame$SeedRuns[i], ', '))
  for (seed in seeds) {
    fileName <- file.path(
      dirVikingResults[paramFrame$DatasetID[i]],
      sprintf(resultFormat, paramFrame$CombnNum[i], seed)
    )
    
    if (file.exists(fileName)) {
      temp <- load(fileName)
      temp <- eval(parse(text = temp)) # Get objects.
      
      if (is.list(temp) && "Result" %in% names(temp)) {
        
        if (is.data.frame(temp$Result))
          community <- temp$Result$Community[[nrow(temp$Result)]]
        else 
          community <- temp$Result
        
        if (length(community) > 100) {
          print(paste(i, length(community), seed))
        }
        
        size <- toString(length(community))
        
        if (community[1] != "") 
          abund <- toString(temp$Abund[community + 1])
        else 
          abund <- ""
        
        community <- toString(community)
        
        if (community == "") {
          resultsList$`No State` <- resultsList$`No State` + 1
          resultsSize$`0` <- resultsSize$`0` + 1
          
        } else if (community %in% names(resultsList)) {
          resultsList[[community]] <- resultsList[[community]] + 1
          resultsSize[[size]] <- resultsSize[[size]] + 1
          
        } else {
          resultsList[[community]] <- 1
          resultsAssembly[[community]] <- temp
          resultsAbund[[community]] <- abund
          
          if (size %in% resultsSize) {
            resultsSize[[size]] <- resultsSize[[size]] + 1
          } else {
            resultsSize[[size]] <- 1
          }
        }
      } else {
        resultsList$`No State` <- resultsList$`No State` + 1
        resultsSize$`0` <- resultsSize$`0` + 1
      }
    } else {
      resultsList$`No Run` <- resultsList$`No Run` + 1
      resultsSize$`0` <- resultsSize$`0` + 1
    }
  }
  
  paramFrame$EndStates[[i]] <- resultsList
  paramFrame$EndStatesNum[i] <- length(resultsList) - 2 # ! No State, No Run
  paramFrame$EndStateSizes[[i]] <- resultsSize
  paramFrame$EndStateSizesNum[i] <- length(resultsSize) - 1 # ! 0
  paramFrame$EndStateAssembly[[i]] <- resultsAssembly
  paramFrame$EndStateAbundance[[i]] <- resultsAbund
}
[1] "40 102 26674330.2810937"
[1] "40 102 39581658.8308662"
[1] "40 102 62868591.6075483"
[1] "40 102 56308729.4343859"
[1] "40 102 19815561.3383278"
[1] "40 102 18465117.5979525"
[1] "40 102 26095917.3552692"
[1] "40 102 60475607.9846993"
[1] "40 102 69888928.7887141"
[1] "40 102 99290275.8764103"
[1] "40 102 9528546.3379696"
[1] "40 102 93055356.4801812"
[1] "40 102 68074182.04844"
[1] "40 102 98843143.6475366"
[1] "40 102 46835659.747012"
[1] "40 102 73302648.1466368"
[1] "40 102 57818840.3043896"
[1] "40 102 16893954.4353634"
[1] "40 102 34339508.8333637"
[1] "40 102 51167502.5569275"
[1] "40 102 22425330.9192136"
[1] "40 102 85563534.0558365"
[1] "40 102 56022880.6221858"
[1] "40 102 26422126.0789782"
[1] "40 102 35608488.8568148"
[1] "40 102 11042673.8159731"
[1] "40 102 83468216.2618265"
[1] "40 102 40178918.7919348"
[1] "40 102 19817898.0965167"
[1] "40 102 98183176.6199321"
[1] "40 102 91958070.2902749"
[1] "40 102 19873716.2863836"
[1] "40 102 71078193.699941"
[1] "40 102 99907531.9850817"
[1] "40 102 51399637.3396367"
[1] "40 102 41323695.4482272"
[1] "40 102 23621508.1531554"
[1] "40 102 71196689.6662489"
[1] "40 102 37134249.3221164"
[1] "40 102 64464564.4584671"
[1] "40 102 81891857.2738767"
[1] "40 102 25338337.9429579"
[1] "40 102 78799370.9789589"
[1] "40 102 9775842.88921207"
[1] "40 102 63896748.4002933"
[1] "40 102 7201567.82772392"
[1] "40 102 29442481.4870581"
[1] "40 102 10879790.9459099"
[1] "40 102 39187487.0983884"
[1] "40 102 44344145.3855485"
[1] "40 102 2694217.69771725"
[1] "40 102 58244740.8000007"
[1] "40 102 28041951.241903"
[1] "40 102 32239883.5560307"
[1] "40 102 22650681.8784401"
[1] "40 102 2473517.92640984"
[1] "40 102 68456584.3315795"
[1] "40 102 5582987.0980233"
[1] "40 102 48389758.2860664"
[1] "40 102 27699859.6265912"
[1] "40 102 55826936.5224987"
[1] "40 102 36236764.6303028"
[1] "40 102 82180834.6780017"
[1] "40 102 11491039.1857848"
[1] "40 102 689778.942614794"
[1] "40 102 66809097.7473184"
[1] "40 102 31505824.4392276"
[1] "40 102 79718462.9132971"
[1] "40 102 77086616.5170446"
[1] "40 102 76446791.1561951"
[1] "40 102 98389471.7181101"
[1] "40 102 78609766.2290558"
[1] "40 102 46250235.2427691"
[1] "40 102 19806321.3611022"
[1] "40 102 58000172.3021269"
[1] "40 102 93652395.8807811"
[1] "40 102 40964502.7481019"
[1] "40 102 30762933.1946373"
[1] "40 102 74916414.2878726"
[1] "40 102 69335297.6348251"
[1] "40 102 12855840.0087059"
[1] "40 102 49537618.8773662"
[1] "40 102 78466867.4459681"
[1] "40 102 16305246.2274209"
[1] "40 102 95725073.6420974"
[1] "40 102 50715170.4281569"
[1] "40 102 23608537.716791"
[1] "40 102 72722480.0502881"
[1] "40 102 94330947.380513"
[1] "40 102 63244247.9021847"
[1] "40 102 10481487.493962"
[1] "40 102 96970770.1122388"
[1] "40 102 71544740.581885"
[1] "40 102 3593988.85630071"
[1] "40 102 11239372.7293238"
[1] "40 102 46830266.6442469"
[1] "40 102 9484747.58770317"
[1] "40 102 58595952.9271349"
[1] "40 102 36778931.4361289"
[1] "40 102 7347386.53991371"
[1] "46 120 81955084.3443722"
[1] "47 320 95541944.145225"
[1] "47 320 8161125.23898482"
[1] "47 320 39323352.4635434"
[1] "47 320 80091710.0161314"
[1] "47 320 58265319.9788183"
[1] "47 320 67721453.2392099"
[1] "47 320 86982388.1192133"
[1] "47 320 80757901.3518989"
[1] "47 320 28097170.7077697"
[1] "47 320 51082268.1244463"
[1] "47 320 33807065.1283488"
[1] "48 1020 98442707.6997235"

Plot Preparation

# X, Y, Basal and Consumer.
# Z = Sizes of the Endstates.

plotScalingData <- data.frame(
  CombnNum = rep(paramFrame$CombnNum, paramFrame$EndStatesNum),
  Basals = rep(paramFrame$Basals, paramFrame$EndStatesNum),
  Consumers = rep(paramFrame$Consumers, paramFrame$EndStatesNum),
  Dataset = rep(paramFrame$Dataset, paramFrame$EndStatesNum),
  DatasetID = rep(paramFrame$DatasetID, paramFrame$EndStatesNum)
)

# Communities
comms <- unlist(lapply(paramFrame$EndStates, names))
freqs <- unlist(paramFrame$EndStates)
asmbl <- unlist(paramFrame$EndStateAssembly, recursive = FALSE)
asmbl <- asmbl[comms != "No Run" & comms != "No State"]
freqs <- freqs[comms != "No Run" & comms != "No State"]
comms <- comms[comms != "No Run" & comms != "No State"]

asmbl <- lapply(asmbl, function(d) {
  if (is.null(d)) return(NA)
  if ("Result.Outcome" %in% names(d))
    d %>% dplyr::filter(Result.Outcome != "Type 1 (Failure)" & 
                          Result.Outcome != "Present")
  else
    d$Result %>% dplyr::filter(Outcome != "Type 1 (Failure)" & 
                                 Outcome != "Present")
})

plotScalingData$Communities <- comms
plotScalingData$CommunityFreq <- freqs
plotScalingData$CommunitySeq <- asmbl

# Community Size
temp <- unlist(lapply(strsplit(plotScalingData$Communities, ','), length))
plotScalingData$CommunitySize <- temp

candidateData <- plotScalingData %>% dplyr::group_by(
  CombnNum, Dataset
) %>% dplyr::mutate(
  OtherSteadyStates = dplyr::n() - 1
)

Pools and Matrices

mats <- list()
poolsall <- list() # name pools used in save data; be careful!

for (i in 1:length(dirViking)) {
  temp <- load(file.path(
    dirViking[i], 
    paste0("LawMorton1996-NumericalPoolCommunityScaling-PoolMats", 
           c(9, 10)[i], 
           ".RDS")
  ))
  mats[[i]] <- eval(parse(text = temp[1]))
  poolsall[[i]] <- eval(parse(text = temp[2]))
}
pools <- poolsall

Abundances

# First, check if it is in the paramFrame.
# Second, check if it is in the saved data from the previous.
# Otherwise, ignore it, we'll figure out what it is and why it is missing later.

candidateData$CommunityAbund <- ""

for (r in 1:nrow(candidateData)) {
  # ID 1:4 are used to identify paramFrame, 5 used to identify abundance
  ID <- candidateData[r, 1:6]
  paramFrameRow <- paramFrame %>% dplyr::filter(
    CombnNum == ID$CombnNum,
    Basals == ID$Basals,
    Consumers == ID$Consumers,
    Dataset == ID$Dataset
  )
  
  if (is.list(paramFrameRow$EndStateAbundance[[1]])) {
    entry <- which(ID$Communities == names(paramFrameRow$EndStateAbundance[[1]]))
    if (length(entry)) {
      candidateData$CommunityAbund[r] <- paramFrameRow$EndStateAbundance[[1]][[entry]]
      next()
    }
  }
}
print(paste("Failures:", 
            sum(candidateData$CommunityAbund %in% 
                  c("", "Failure", "EstimatedFailure"))))
[1] "Failures: 0"
candidateData <- candidateData %>% dplyr::filter(CommunityAbund != "",
                                                 CommunityAbund != "Failure",
                                                 CommunityAbund != "EstimateFailure")
candidateData$CommunityProd <- NA
for (r in 1:nrow(candidateData)) {
  candidateData$CommunityProd[r] <- with(
    candidateData[r, ], 
    RMTRCode2::Productivity(
      Pool = pools[[DatasetID]][[CombnNum]], 
      InteractionMatrix = mats[[DatasetID]][[CombnNum]], 
      Community = Communities, 
      Populations = CommunityAbund
    )
  )
}
print(paste("Numerically Unstable:", 
            sum(candidateData$CommunityProd > 10^10 
                  )))
[1] "Numerically Unstable: 7"
candidateData <- candidateData %>% dplyr::filter(CommunityProd < 10^10)

Communities

# For usage by the reader.

plotScaling <- plotly::plot_ly(
  candidateData,
  x = ~Basals,
  y = ~Consumers,
  z = ~CommunitySize,
  color = ~Dataset,
  colors = c("red", "blue", "black")
)

plotScaling <- plotly::add_markers(plotScaling)

plotScaling <- plotly::layout(
  plotScaling,
  scene = list(
    xaxis = list(type = "log"),
    yaxis = list(type = "log"),
    camera = list(
      eye = list(
        x = -1.25, y = -1.25, z = .05
      )
    )
  )
)

plotScaling

Communities Data

candidateData

Foodwebs, Prep

foodWebs <- list()

for (r in 1:nrow(candidateData)) {
  foodWebs[[r]] <- with(
    candidateData[r, ],
    {
      redCom <- RMTRCode2::CsvRowSplit(Communities)
      redMat <- mats[[DatasetID]][[CombnNum]][redCom, redCom]
      redPool <- pools[[DatasetID]][[CombnNum]][redCom, ]
      
      colnames(redMat) <- paste0('s',as.character(redCom))
      rownames(redMat) <- colnames(redMat)
      
      names(redPool)[1] <- "node"
      redPool$node <- colnames(redMat)
      names(redPool)[3] <- "M"
      
      Graph <- igraph::graph_from_adjacency_matrix(
        redMat, weighted = TRUE
      )
      
      Graph <- igraph::set.vertex.attribute(
        Graph, "name", value = colnames(redMat)
      )
      
      redPool$N <- RMTRCode2::CsvRowSplit(CommunityAbund)
      
      # For later analysis, take the matrix diagonal.
      
      redPool$Intraspecific <- diag(redMat)
      
      GraphAsDataFrame <- igraph::as_data_frame(Graph)
  
      # Add in abundances for calculating abundance * (gain or loss)
      GraphAsDataFrame <- dplyr::left_join(
        GraphAsDataFrame,
        dplyr::select(redPool, node, N),
        by = c("to" = "node")
      )
  
      # Split data frame.
      ResCon <- GraphAsDataFrame[GraphAsDataFrame$weight > 0,]
      ConRes <- GraphAsDataFrame[GraphAsDataFrame$weight < 0,]
      
      # Reorder and rename variables.
      ResCon <- dplyr::select(ResCon, 
                                 to, from, # resource = to, consumer = from, 
                                 effectPerUnit = weight, resourceAbund = N)
      ConRes <- dplyr::select(ConRes, 
                                 to, from, # resource = from, consumer = to, 
                                 effectPerUnit = weight, consumerAbund = N)
      ResCon <- dplyr::mutate(dplyr::group_by(ResCon, from),
                              effectActual = effectPerUnit * resourceAbund,
                              Type = "Exploit+")
      ConRes <- dplyr::mutate(dplyr::group_by(ConRes, from),
                              effectActual = effectPerUnit * consumerAbund,
                              Type = ifelse(from == to,
                                            "SelfReg-",
                                            "Exploit-"))
      
      IntriG <- with(redPool, data.frame(
                              from = node, #resource = node,
                              to = node, #consumer = node,
                              effectPerUnit = ifelse(ReproductionRate > 0,
                                                   ReproductionRate, 0),
                              effectActual = ifelse(ReproductionRate > 0,
                                                  N * ReproductionRate, 0),
                              Type = "Intrisc+")) 
      IntriL <- with(redPool, data.frame(
                              from = node, #resource = node,
                              to = node, #consumer = node,
                              effectPerUnit = ifelse(ReproductionRate < 0,
                                                   ReproductionRate, 0),
                              effectActual = ifelse(ReproductionRate < 0,
                                                  N * ReproductionRate, 0),
                              Type = "Intrisc-"))
      
      EdgeDataFrame <- dplyr::bind_rows(
        dplyr::select(ResCon, -resourceAbund), 
        dplyr::select(ConRes, -consumerAbund),
        IntriG, IntriL
      )
      
      EdgeDataFrame <- EdgeDataFrame %>% dplyr::rename(
        # Empirically speaking, to and from appear reversed.
        # A consumer (from) should have a negative effect on resource (to),
        # but the organisation so far marks it as positive. We fix this.
        tempname = to,
        to = from
      ) %>% dplyr::rename(
        from = tempname
      ) %>% dplyr::filter(
        # Remove placeholder entries
        effectPerUnit != 0
      ) %>% dplyr::mutate(
        # Useful to keep effects separate
        effectSign = sign(effectPerUnit)
      ) %>% group_by(
        to, effectSign
      ) %>% dplyr::mutate(
        # Perform the post mortem of the most influential from's
        effectEfficiency = effectPerUnit / sum(effectPerUnit), 
        effectNormalised = effectActual / sum(effectActual)
      ) %>% dplyr::arrange(to)
      
      list(
        Edges = EdgeDataFrame,
        Vertices = redPool
      )
    }
  )
}
toCheddar <- function(EVList, name = "") {# Edges Vertices List
  links <- EVList$Edges

  # cheddar does not like "cannibalism".
  links <- links[
    links$to != links$from,
  ]

  # "[C]olumns called ‘resource’ and ‘consumer’ must be given."
  links <- dplyr::bind_rows(
    links %>% dplyr::filter(effectSign == 1) %>% dplyr::rename(
      resource = from, consumer = to),
    links %>% dplyr::filter(effectSign == -1) %>% dplyr::rename(
      resource = to, consumer = from),
  ) %>% dplyr::select(-Type) # Cheddar confuses node Type and edge Type.

  cheddar::Community(
    nodes = EVList$Vertices,
    properties = list(
      title = name,
      M.units = "masses",
      N.units = "abund"
    ),
    trophic.links = links
  )
}

toIGraph <- function(EVList, sign = 0) {
  igraph::graph_from_data_frame(
    d = if(sign == 0) {
      EVList$Edges
    } else {
      EVList$Edges[EVList$Edges$effectSign == sign, ]
    },
    directed = TRUE,
    vertices = EVList$Vertices
  )
}


toPostMortem <- function(EVList,
                         threshold = 0, # sets to minimal size edges below
                         nodeSize = c("None", "Abundance", "Size"),
                         edgeScale = 10,
                         reducedTrophic = TRUE) {
  if (tolower(threshold) == "adaptive") {
    threshold = EVList$Edges %>% group_by(
      to, effectSign
    ) %>% summarise(
      max = max(effectNormalised), .groups = "drop"
    ) %>% ungroup %>% pull(max) %>% min
  }

  theGc <- toCheddar(EVList, name = "Trophic Levels")
  theGi <- toIGraph(EVList)

  theGiGain <- toIGraph(EVList, sign = 1)
  theGiLoss <- toIGraph(EVList, sign = -1)

  theLayout <- igraph::layout.circle(theGi)

  theSize <- match.arg(nodeSize, c("Abundance", "Size", "None"))
  if (theSize == "Abundance")
    theVs <- sqrt(igraph::vertex_attr(theGi)$N) * 10
  else if (theSize == "Size") {
    theVs <- igraph::vertex_attr(theGi)$M
    theVs <- sqrt(theVs / min(theVs)) * 10
  } else if (theSize == "None") {
    theVs <- 15
  }

  theColors <- ifelse(
    igraph::vertex_attr(theGi)$Type == "Basal", "skyblue", "red"
  )
  if ("Core" %in% names(igraph::vertex_attr(theGi))) {
    theShapes <- ifelse(igraph::vertex_attr(theGi)$Core,
                        0,
                        1)
  } else {
    theShapes <- 1 
    # Note Igraph uses "circle" then "rectangle",
    # but R and cheddar use "rectangle" then "circle", so we will use a !.
  }

  theBoth <- igraph::edge_attr(theGi)$effectNormalised
  theGain <- igraph::edge_attr(theGiGain)$effectNormalised
  theLoss <- igraph::edge_attr(theGiLoss)$effectNormalised

  theBoth[theBoth < threshold] <- 0
  theGain[theGain < threshold] <- 0
  theLoss[theLoss < threshold] <- 0

  # Inform the graphs of which edges are not needed.
  theGi <- igraph::delete_edges(theGi, which(theBoth == 0))
  theGiGain <- igraph::delete_edges(theGiGain, which(theGain == 0))
  theGiLoss <- igraph::delete_edges(theGiLoss, which(theLoss == 0))

  # Remove the same entries so that lengths match.
  theGain <- theGain[theGain > 0]
  theLoss <- theLoss[theLoss > 0]

  theGain <- theGain * edgeScale
  theLoss <- theLoss * edgeScale

  parold <- par(no.readonly = TRUE)
  par(mfrow = c(2, 2), # Two Rows, Two Columns
      mar = c(0, 1.5, 1, 0), # Margins, bottom, left, top, right
      oma = c(0.1, 0.1, 0.1, 0.1) # Outer margins.
  )

  cheddar::PlotWebByLevel(
    theGc,
    show.level.lines = TRUE,
    # Had been using LongWeighted, but that seems to give the upside down T.
    # Flow based seems to be more what we are expecting, given the usage of
    # thresholding and what that shows. The flows here are expected to be
    # flows of energy through the food web.
    level = cheddar::FlowBasedTrophicLevel(theGc, weight.by = "effectNormalised"),
      col = theColors,
      pch = theShapes
  )

  if (!reducedTrophic) {
    plot(
      theGi,
      layout = theLayout,
      vertex.size = theVs,
      edge.width = 1,
      edge.arrow.size = 0.3,
      edge.arrow.width = 1,
      vertex.color = theColors,
      vertex.shape = igraph::shapes()[as.numeric(!theShapes) + 1],
      edge.lty = 2,
      edge.color = "grey",
      edge.arrow.mode = ">",
      main = "Consumption"
    )
  } else {
    EVListRed <- EVList
    EVListRed$Edges <- EVListRed$Edges %>% dplyr::filter(
      effectNormalised >= threshold
    )
    theGc2 <- toCheddar(EVListRed, name = "Strongest Trophic Levels")
    cheddar::PlotWebByLevel(
      theGc2,
      show.level.lines = TRUE,
    level = cheddar::FlowBasedTrophicLevel(theGc2, weight.by = "effectNormalised"),
      col = theColors,
      pch = theShapes
    )
  }

  plot(
    theGiGain,
    layout = theLayout,
    vertex.size = theVs,
    edge.width = theGain,
    edge.arrow.size = 0.3,
    edge.arrow.width = 1,
    vertex.color = theColors,
      vertex.shape = igraph::shapes()[as.numeric(!theShapes) + 1],
    edge.lty = 2,
    edge.color = "blue",
    edge.arrow.mode = ">",
    main = "Consumer's Gains"
  )

  plot(
    theGiLoss,
    layout = theLayout,
    vertex.size = theVs,
    edge.width = theLoss,
    edge.arrow.size = 0.3,
    edge.arrow.width = 2,
    vertex.color = theColors,
      vertex.shape = igraph::shapes()[as.numeric(!theShapes) + 1],
    edge.lty = 3,
    edge.color = "darkred",
    edge.arrow.mode = "<",
    main = "Resource's Losses"
  )
  
  par(parold)
  
  EVList$Edges %>% dplyr::ungroup() %>% dplyr::filter(
    effectNormalised >= threshold
  ) %>% dplyr::select(
    -effectSign
  ) %>% dplyr::arrange(
    to, -effectNormalised
  )
}

Foodwebs, Overlapping

1

i <- 1
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

2

i <- 2
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

3

i <- 3
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

4

i <- 4
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

5

i <- 5
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

6

i <- 6
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

7

i <- 7
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

8

i <- 8
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

Foodwebs, Larger Food Preference

9

i <- 9
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

10

i <- 10
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

11

i <- 11
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

12

i <- 12
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

13

i <- 13
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

14

i <- 14
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

15

i <- 15
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

16

i <- 16
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

17

i <- 17
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

18

i <- 18
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp

19

i <- 19
toPostMortem(foodWebs[[i]], nodeSize = "None", threshold = "Adaptive") -> temp

temp
LS0tDQp0aXRsZTogIkFuc3dlcmluZyBRdWVzdGlvbnM7IEdhdGhlciBEYXRhLCBWaWtpbmcgOToxMCwgMjAyMS0wOCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCi0tLQ0KDQpgYGB7ciBsaWJzLCBtZXNzYWdlPUZBTFNFfQ0KIyBDaGVjayByZXF1aXNpdGUgcGFja2FnZXMgYXJlIGluc3RhbGxlZC4NCnBhY2thZ2VzIDwtIGMoDQogICJwbG90bHkiLCANCiAgImRwbHlyIg0KKQ0KZm9yIChwa2cgaW4gcGFja2FnZXMpIHsNCiAgbGlicmFyeShwa2csIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCn0NCmBgYA0KDQojIFRhYnMgey50YWJzZXR9DQoNCiMjIExvYWQNClB1bGxpbmcgY29kZSBhbG1vc3QgZGlyZWN0bHkgZnJvbSBgTE0xOTk2LU51bVBvb2xDb21TY2FsaW5nLVJlc3VsdHMtMjAyMS0wNS5SbWRgLg0KYGBge3IgZGlyc30NCmRpclZpa2luZyA8LSBjKA0KICBmaWxlLnBhdGgoDQogICAgZ2V0d2QoKSwgIkxDQUJfTGF3TW9ydG9uMTk5Ni1OdW1lcmljYWxQb29sQ29tbXVuaXR5U2NhbGluZzkiDQogICksDQogIGZpbGUucGF0aCgNCiAgICBnZXR3ZCgpLCAiTENBQl9MYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nMTAiDQogICkNCikNCmRpclZpa2luZ1Jlc3VsdHMgPC0gZmlsZS5wYXRoKA0KICBkaXJWaWtpbmcsIGMoInNhdmUtMjAyMS0wOC0wMyIpICMgTGF0dGVyIG5vdCAxMDAlIHlldC4NCikNCnJlc3VsdEZvcm1hdCA8LSBwYXN0ZTAoDQogICJydW4tIiwgDQogICIlZCIsICMgQ29tYmluYXRpb24gTnVtYmVyLCBvciBDb21ibk51bS4NCiAgIi0iLCANCiAgIiVzIiwgIyBSdW4gU2VlZC4NCiAgIi5SRFMiDQopDQpgYGANCg0KYGBge3Igb3JnYW5pc2VQYXJhbXN9DQpzb3VyY2UoDQogIGZpbGUucGF0aChnZXR3ZCgpLCANCiAgICAgICAgICAgICJMYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nLVNldHRpbmdzOS5SIikNCikNCg0KcGFyYW1GcmFtZSA8LSB3aXRoKGxpc3QoDQogIGIgPSByZXAoYmFzYWwsIHRpbWVzID0gbGVuZ3RoKGNvbnN1bWVyKSksDQogIGMgPSByZXAoY29uc3VtZXIsIGVhY2ggPSBsZW5ndGgoYmFzYWwpKSwNCiAgczEgPSBzZWVkc1ByZXBbMToobGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikpXSwNCiAgczIgPSBzZWVkc1ByZXBbDQogICAgKGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpICsgMSk6KA0KICAgICAgMiAqIGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKQ0KICBdLA0KICBzUiA9IHNlZWRzUnVuDQopLCB7DQogIHRlbXAgPC0gZGF0YS5mcmFtZSgNCiAgICBDb21ibk51bSA9IDAsDQogICAgQmFzYWxzID0gYiwNCiAgICBDb25zdW1lcnMgPSBjLA0KICAgIFNlZWRQb29sID0gczEsDQogICAgU2VlZE1hdCA9IHMyLA0KICAgIFNlZWRSdW5zID0gIiIsDQogICAgU2VlZFJ1bnNOdW0gPSAwLA0KICAgIEVuZFN0YXRlcyA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgoYikpKSwNCiAgICBFbmRTdGF0ZXNOdW0gPSAwLA0KICAgIEVuZFN0YXRlU2l6ZXMgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVTaXplc051bSA9IE5BLA0KICAgIEVuZFN0YXRlQXNzZW1ibHkgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVBYnVuZGFuY2UgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRGF0YXNldCA9ICJWaWtpbmc5IiwNCiAgICBEYXRhc2V0SUQgPSAxLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQogIGZvciAoaSBpbiAxOm5yb3codGVtcCkpIHsNCiAgICBzZWVkcyA8LSBzUlsoKGkgLSAxKSAqIHJ1bnMgKyAxKSA6IChpICogcnVucyldDQogICAgdGVtcCRTZWVkUnVuc1tpXSA8LSB0b1N0cmluZyhzZWVkcykgIyBDU1YNCiAgICB0ZW1wJFNlZWRSdW5zTnVtW2ldIDwtIGxlbmd0aChzZWVkcykNCiAgfQ0KICB0ZW1wJENvbWJuTnVtIDwtIDE6bnJvdyh0ZW1wKQ0KICB0ZW1wDQp9KQ0KYGBgDQoNCmBgYHtyIGxvYWRSZXN1bHRzfQ0KIyBOb3RlOiBuICsgMiBlbmQgc3RhdGVzLiBGYWlsdXJlIHRvIGZpbmlzaCwgZmFpbHVyZSB0byBvYnRhaW4gc3RhdGUsIGFuZCBzdGF0ZS4NCmZvciAoaSBpbiAxOm5yb3cocGFyYW1GcmFtZSkpIHsNCiAgcmVzdWx0c0xpc3QgPC0gbGlzdCgNCiAgICAiTm8gUnVuIiA9IDAsDQogICAgIk5vIFN0YXRlIiA9IDANCiAgKQ0KICByZXN1bHRzU2l6ZSA8LSBsaXN0KA0KICAgICIwIiA9IDANCiAgKQ0KICByZXN1bHRzQXNzZW1ibHkgPC0gbGlzdCgNCiAgICAiTm8gUnVuIiA9IGRhdGEuZnJhbWUoKSwNCiAgICAiTm8gU3RhdGUiID0gZGF0YS5mcmFtZSgpDQogICkNCiAgcmVzdWx0c0FidW5kIDwtIGxpc3QoDQogICAgIk5vIFJ1biIgPSAiIiwNCiAgICAiTm8gU3RhdGUiID0gIiINCiAgKQ0KICBzZWVkcyA8LSB1bmxpc3Qoc3Ryc3BsaXQocGFyYW1GcmFtZSRTZWVkUnVuc1tpXSwgJywgJykpDQogIGZvciAoc2VlZCBpbiBzZWVkcykgew0KICAgIGZpbGVOYW1lIDwtIGZpbGUucGF0aCgNCiAgICAgIGRpclZpa2luZ1Jlc3VsdHNbcGFyYW1GcmFtZSREYXRhc2V0SURbaV1dLA0KICAgICAgc3ByaW50ZihyZXN1bHRGb3JtYXQsIHBhcmFtRnJhbWUkQ29tYm5OdW1baV0sIHNlZWQpDQogICAgKQ0KICAgIA0KICAgIGlmIChmaWxlLmV4aXN0cyhmaWxlTmFtZSkpIHsNCiAgICAgIHRlbXAgPC0gbG9hZChmaWxlTmFtZSkNCiAgICAgIHRlbXAgPC0gZXZhbChwYXJzZSh0ZXh0ID0gdGVtcCkpICMgR2V0IG9iamVjdHMuDQogICAgICANCiAgICAgIGlmIChpcy5saXN0KHRlbXApICYmICJSZXN1bHQiICVpbiUgbmFtZXModGVtcCkpIHsNCiAgICAgICAgDQogICAgICAgIGlmIChpcy5kYXRhLmZyYW1lKHRlbXAkUmVzdWx0KSkNCiAgICAgICAgICBjb21tdW5pdHkgPC0gdGVtcCRSZXN1bHQkQ29tbXVuaXR5W1tucm93KHRlbXAkUmVzdWx0KV1dDQogICAgICAgIGVsc2UgDQogICAgICAgICAgY29tbXVuaXR5IDwtIHRlbXAkUmVzdWx0DQogICAgICAgIA0KICAgICAgICBpZiAobGVuZ3RoKGNvbW11bml0eSkgPiAxMDApIHsNCiAgICAgICAgICBwcmludChwYXN0ZShpLCBsZW5ndGgoY29tbXVuaXR5KSwgc2VlZCkpDQogICAgICAgIH0NCiAgICAgICAgDQogICAgICAgIHNpemUgPC0gdG9TdHJpbmcobGVuZ3RoKGNvbW11bml0eSkpDQogICAgICAgIA0KICAgICAgICBpZiAoY29tbXVuaXR5WzFdICE9ICIiKSANCiAgICAgICAgICBhYnVuZCA8LSB0b1N0cmluZyh0ZW1wJEFidW5kW2NvbW11bml0eSArIDFdKQ0KICAgICAgICBlbHNlIA0KICAgICAgICAgIGFidW5kIDwtICIiDQogICAgICAgIA0KICAgICAgICBjb21tdW5pdHkgPC0gdG9TdHJpbmcoY29tbXVuaXR5KQ0KICAgICAgICANCiAgICAgICAgaWYgKGNvbW11bml0eSA9PSAiIikgew0KICAgICAgICAgIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgPC0gcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCArIDENCiAgICAgICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgICAgICAgIA0KICAgICAgICB9IGVsc2UgaWYgKGNvbW11bml0eSAlaW4lIG5hbWVzKHJlc3VsdHNMaXN0KSkgew0KICAgICAgICAgIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSA8LSByZXN1bHRzTGlzdFtbY29tbXVuaXR5XV0gKyAxDQogICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSByZXN1bHRzU2l6ZVtbc2l6ZV1dICsgMQ0KICAgICAgICAgIA0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSA8LSAxDQogICAgICAgICAgcmVzdWx0c0Fzc2VtYmx5W1tjb21tdW5pdHldXSA8LSB0ZW1wDQogICAgICAgICAgcmVzdWx0c0FidW5kW1tjb21tdW5pdHldXSA8LSBhYnVuZA0KICAgICAgICAgIA0KICAgICAgICAgIGlmIChzaXplICVpbiUgcmVzdWx0c1NpemUpIHsNCiAgICAgICAgICAgIHJlc3VsdHNTaXplW1tzaXplXV0gPC0gcmVzdWx0c1NpemVbW3NpemVdXSArIDENCiAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSAxDQogICAgICAgICAgfQ0KICAgICAgICB9DQogICAgICB9IGVsc2Ugew0KICAgICAgICByZXN1bHRzTGlzdCRgTm8gU3RhdGVgIDwtIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgKyAxDQogICAgICAgIHJlc3VsdHNTaXplJGAwYCA8LSByZXN1bHRzU2l6ZSRgMGAgKyAxDQogICAgICB9DQogICAgfSBlbHNlIHsNCiAgICAgIHJlc3VsdHNMaXN0JGBObyBSdW5gIDwtIHJlc3VsdHNMaXN0JGBObyBSdW5gICsgMQ0KICAgICAgcmVzdWx0c1NpemUkYDBgIDwtIHJlc3VsdHNTaXplJGAwYCArIDENCiAgICB9DQogIH0NCiAgDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVzW1tpXV0gPC0gcmVzdWx0c0xpc3QNCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW1baV0gPC0gbGVuZ3RoKHJlc3VsdHNMaXN0KSAtIDIgIyAhIE5vIFN0YXRlLCBObyBSdW4NCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZVNpemVzW1tpXV0gPC0gcmVzdWx0c1NpemUNCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZVNpemVzTnVtW2ldIDwtIGxlbmd0aChyZXN1bHRzU2l6ZSkgLSAxICMgISAwDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVBc3NlbWJseVtbaV1dIDwtIHJlc3VsdHNBc3NlbWJseQ0KICBwYXJhbUZyYW1lJEVuZFN0YXRlQWJ1bmRhbmNlW1tpXV0gPC0gcmVzdWx0c0FidW5kDQp9DQpgYGANCg0KYGBge3Igb3JnYW5pc2VQYXJhbXMyfQ0Kc291cmNlKA0KICBmaWxlLnBhdGgoZ2V0d2QoKSwgDQogICAgICAgICAgICAiTGF3TW9ydG9uMTk5Ni1OdW1lcmljYWxQb29sQ29tbXVuaXR5U2NhbGluZy1TZXR0aW5nczEwLlIiKQ0KKQ0KDQpvbGROcm93IDwtIG5yb3cocGFyYW1GcmFtZSkNCg0KcGFyYW1GcmFtZSA8LSByYmluZChwYXJhbUZyYW1lLCB3aXRoKGxpc3QoDQogIGIgPSByZXAoYmFzYWwsIHRpbWVzID0gbGVuZ3RoKGNvbnN1bWVyKSksDQogIGMgPSByZXAoY29uc3VtZXIsIGVhY2ggPSBsZW5ndGgoYmFzYWwpKSwNCiAgczEgPSBzZWVkc1ByZXBbMToobGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikpXSwNCiAgczIgPSBzZWVkc1ByZXBbDQogICAgKGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpICsgMSk6KA0KICAgICAgMiAqIGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKQ0KICBdLA0KICBzUiA9IHNlZWRzUnVuDQopLCB7DQogIHRlbXAgPC0gZGF0YS5mcmFtZSgNCiAgICBDb21ibk51bSA9IDAsDQogICAgQmFzYWxzID0gYiwNCiAgICBDb25zdW1lcnMgPSBjLA0KICAgIFNlZWRQb29sID0gczEsDQogICAgU2VlZE1hdCA9IHMyLA0KICAgIFNlZWRSdW5zID0gIiIsDQogICAgU2VlZFJ1bnNOdW0gPSAwLA0KICAgIEVuZFN0YXRlcyA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgoYikpKSwNCiAgICBFbmRTdGF0ZXNOdW0gPSAwLA0KICAgIEVuZFN0YXRlU2l6ZXMgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVTaXplc051bSA9IE5BLA0KICAgIEVuZFN0YXRlQXNzZW1ibHkgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVBYnVuZGFuY2UgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRGF0YXNldCA9ICJWaWtpbmcxMCIsDQogICAgRGF0YXNldElEID0gMiwNCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgKQ0KICBmb3IgKGkgaW4gMTpucm93KHRlbXApKSB7DQogICAgc2VlZHMgPC0gc1JbKChpIC0gMSkgKiBydW5zICsgMSkgOiAoaSAqIHJ1bnMpXQ0KICAgIHRlbXAkU2VlZFJ1bnNbaV0gPC0gdG9TdHJpbmcoc2VlZHMpICMgQ1NWDQogICAgdGVtcCRTZWVkUnVuc051bVtpXSA8LSBsZW5ndGgoc2VlZHMpDQogIH0NCiAgdGVtcCRDb21ibk51bSA8LSAxOm5yb3codGVtcCkNCiAgdGVtcA0KfSkNCikNCmBgYA0KDQpgYGB7ciBsb2FkUmVzdWx0czJ9DQojIE5vdGU6IG4gKyAyIGVuZCBzdGF0ZXMuIEZhaWx1cmUgdG8gZmluaXNoLCBmYWlsdXJlIHRvIG9idGFpbiBzdGF0ZSwgYW5kIHN0YXRlLg0KIyBNb2RpZmllZCBmcm9tIGFib3ZlLCBidXQgd2l0aCB0aGUgYWJ1bmRhbmNlIHJlY29yZGVkLg0KZm9yIChpIGluIChvbGROcm93ICsgMSk6bnJvdyhwYXJhbUZyYW1lKSkgew0KICByZXN1bHRzTGlzdCA8LSBsaXN0KA0KICAgICJObyBSdW4iID0gMCwNCiAgICAiTm8gU3RhdGUiID0gMA0KICApDQogIHJlc3VsdHNTaXplIDwtIGxpc3QoDQogICAgIjAiID0gMA0KICApDQogIHJlc3VsdHNBc3NlbWJseSA8LSBsaXN0KA0KICAgICJObyBSdW4iID0gZGF0YS5mcmFtZSgpLA0KICAgICJObyBTdGF0ZSIgPSBkYXRhLmZyYW1lKCkNCiAgKQ0KICByZXN1bHRzQWJ1bmQgPC0gbGlzdCgNCiAgICAiTm8gUnVuIiA9ICIiLA0KICAgICJObyBTdGF0ZSIgPSAiIg0KICApDQogIHNlZWRzIDwtIHVubGlzdChzdHJzcGxpdChwYXJhbUZyYW1lJFNlZWRSdW5zW2ldLCAnLCAnKSkNCiAgZm9yIChzZWVkIGluIHNlZWRzKSB7DQogICAgZmlsZU5hbWUgPC0gZmlsZS5wYXRoKA0KICAgICAgZGlyVmlraW5nUmVzdWx0c1twYXJhbUZyYW1lJERhdGFzZXRJRFtpXV0sDQogICAgICBzcHJpbnRmKHJlc3VsdEZvcm1hdCwgcGFyYW1GcmFtZSRDb21ibk51bVtpXSwgc2VlZCkNCiAgICApDQogICAgDQogICAgaWYgKGZpbGUuZXhpc3RzKGZpbGVOYW1lKSkgew0KICAgICAgdGVtcCA8LSBsb2FkKGZpbGVOYW1lKQ0KICAgICAgdGVtcCA8LSBldmFsKHBhcnNlKHRleHQgPSB0ZW1wKSkgIyBHZXQgb2JqZWN0cy4NCiAgICAgIA0KICAgICAgaWYgKGlzLmxpc3QodGVtcCkgJiYgIlJlc3VsdCIgJWluJSBuYW1lcyh0ZW1wKSkgew0KICAgICAgICANCiAgICAgICAgaWYgKGlzLmRhdGEuZnJhbWUodGVtcCRSZXN1bHQpKQ0KICAgICAgICAgIGNvbW11bml0eSA8LSB0ZW1wJFJlc3VsdCRDb21tdW5pdHlbW25yb3codGVtcCRSZXN1bHQpXV0NCiAgICAgICAgZWxzZSANCiAgICAgICAgICBjb21tdW5pdHkgPC0gdGVtcCRSZXN1bHQNCiAgICAgICAgDQogICAgICAgIGlmIChsZW5ndGgoY29tbXVuaXR5KSA+IDEwMCkgew0KICAgICAgICAgIHByaW50KHBhc3RlKGksIGxlbmd0aChjb21tdW5pdHkpLCBzZWVkKSkNCiAgICAgICAgfQ0KICAgICAgICANCiAgICAgICAgc2l6ZSA8LSB0b1N0cmluZyhsZW5ndGgoY29tbXVuaXR5KSkNCiAgICAgICAgDQogICAgICAgIGlmIChjb21tdW5pdHlbMV0gIT0gIiIpIA0KICAgICAgICAgIGFidW5kIDwtIHRvU3RyaW5nKHRlbXAkQWJ1bmRbY29tbXVuaXR5ICsgMV0pDQogICAgICAgIGVsc2UgDQogICAgICAgICAgYWJ1bmQgPC0gIiINCiAgICAgICAgDQogICAgICAgIGNvbW11bml0eSA8LSB0b1N0cmluZyhjb21tdW5pdHkpDQogICAgICAgIA0KICAgICAgICBpZiAoY29tbXVuaXR5ID09ICIiKSB7DQogICAgICAgICAgcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCA8LSByZXN1bHRzTGlzdCRgTm8gU3RhdGVgICsgMQ0KICAgICAgICAgIHJlc3VsdHNTaXplJGAwYCA8LSByZXN1bHRzU2l6ZSRgMGAgKyAxDQogICAgICAgICAgDQogICAgICAgIH0gZWxzZSBpZiAoY29tbXVuaXR5ICVpbiUgbmFtZXMocmVzdWx0c0xpc3QpKSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSArIDENCiAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIHJlc3VsdHNTaXplW1tzaXplXV0gKyAxDQogICAgICAgICAgDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIDENCiAgICAgICAgICByZXN1bHRzQXNzZW1ibHlbW2NvbW11bml0eV1dIDwtIHRlbXANCiAgICAgICAgICByZXN1bHRzQWJ1bmRbW2NvbW11bml0eV1dIDwtIGFidW5kDQogICAgICAgICAgDQogICAgICAgICAgaWYgKHNpemUgJWluJSByZXN1bHRzU2l6ZSkgew0KICAgICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSByZXN1bHRzU2l6ZVtbc2l6ZV1dICsgMQ0KICAgICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIDENCiAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgIH0gZWxzZSB7DQogICAgICAgIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgPC0gcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCArIDENCiAgICAgICAgcmVzdWx0c1NpemUkYDBgIDwtIHJlc3VsdHNTaXplJGAwYCArIDENCiAgICAgIH0NCiAgICB9IGVsc2Ugew0KICAgICAgcmVzdWx0c0xpc3QkYE5vIFJ1bmAgPC0gcmVzdWx0c0xpc3QkYE5vIFJ1bmAgKyAxDQogICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgIH0NCiAgfQ0KICANCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZXNbW2ldXSA8LSByZXN1bHRzTGlzdA0KICBwYXJhbUZyYW1lJEVuZFN0YXRlc051bVtpXSA8LSBsZW5ndGgocmVzdWx0c0xpc3QpIC0gMiAjICEgTm8gU3RhdGUsIE5vIFJ1bg0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNbW2ldXSA8LSByZXN1bHRzU2l6ZQ0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNOdW1baV0gPC0gbGVuZ3RoKHJlc3VsdHNTaXplKSAtIDEgIyAhIDANCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZUFzc2VtYmx5W1tpXV0gPC0gcmVzdWx0c0Fzc2VtYmx5DQogIHBhcmFtRnJhbWUkRW5kU3RhdGVBYnVuZGFuY2VbW2ldXSA8LSByZXN1bHRzQWJ1bmQNCn0NCmBgYA0KDQojIyMgUGxvdCBQcmVwYXJhdGlvbg0KDQpgYGB7ciBwbG90M0R9DQojIFgsIFksIEJhc2FsIGFuZCBDb25zdW1lci4NCiMgWiA9IFNpemVzIG9mIHRoZSBFbmRzdGF0ZXMuDQoNCnBsb3RTY2FsaW5nRGF0YSA8LSBkYXRhLmZyYW1lKA0KICBDb21ibk51bSA9IHJlcChwYXJhbUZyYW1lJENvbWJuTnVtLCBwYXJhbUZyYW1lJEVuZFN0YXRlc051bSksDQogIEJhc2FscyA9IHJlcChwYXJhbUZyYW1lJEJhc2FscywgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW0pLA0KICBDb25zdW1lcnMgPSByZXAocGFyYW1GcmFtZSRDb25zdW1lcnMsIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKSwNCiAgRGF0YXNldCA9IHJlcChwYXJhbUZyYW1lJERhdGFzZXQsIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKSwNCiAgRGF0YXNldElEID0gcmVwKHBhcmFtRnJhbWUkRGF0YXNldElELCBwYXJhbUZyYW1lJEVuZFN0YXRlc051bSkNCikNCg0KIyBDb21tdW5pdGllcw0KY29tbXMgPC0gdW5saXN0KGxhcHBseShwYXJhbUZyYW1lJEVuZFN0YXRlcywgbmFtZXMpKQ0KZnJlcXMgPC0gdW5saXN0KHBhcmFtRnJhbWUkRW5kU3RhdGVzKQ0KYXNtYmwgPC0gdW5saXN0KHBhcmFtRnJhbWUkRW5kU3RhdGVBc3NlbWJseSwgcmVjdXJzaXZlID0gRkFMU0UpDQphc21ibCA8LSBhc21ibFtjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQpmcmVxcyA8LSBmcmVxc1tjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQpjb21tcyA8LSBjb21tc1tjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQoNCmFzbWJsIDwtIGxhcHBseShhc21ibCwgZnVuY3Rpb24oZCkgew0KICBpZiAoaXMubnVsbChkKSkgcmV0dXJuKE5BKQ0KICBpZiAoIlJlc3VsdC5PdXRjb21lIiAlaW4lIG5hbWVzKGQpKQ0KICAgIGQgJT4lIGRwbHlyOjpmaWx0ZXIoUmVzdWx0Lk91dGNvbWUgIT0gIlR5cGUgMSAoRmFpbHVyZSkiICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgIFJlc3VsdC5PdXRjb21lICE9ICJQcmVzZW50IikNCiAgZWxzZQ0KICAgIGQkUmVzdWx0ICU+JSBkcGx5cjo6ZmlsdGVyKE91dGNvbWUgIT0gIlR5cGUgMSAoRmFpbHVyZSkiICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPdXRjb21lICE9ICJQcmVzZW50IikNCn0pDQoNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdGllcyA8LSBjb21tcw0KcGxvdFNjYWxpbmdEYXRhJENvbW11bml0eUZyZXEgPC0gZnJlcXMNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlTZXEgPC0gYXNtYmwNCg0KIyBDb21tdW5pdHkgU2l6ZQ0KdGVtcCA8LSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KHBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdGllcywgJywnKSwgbGVuZ3RoKSkNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlTaXplIDwtIHRlbXANCg0KY2FuZGlkYXRlRGF0YSA8LSBwbG90U2NhbGluZ0RhdGEgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgQ29tYm5OdW0sIERhdGFzZXQNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIE90aGVyU3RlYWR5U3RhdGVzID0gZHBseXI6Om4oKSAtIDENCikNCmBgYA0KDQojIyMgUG9vbHMgYW5kIE1hdHJpY2VzDQpgYGB7cn0NCm1hdHMgPC0gbGlzdCgpDQpwb29sc2FsbCA8LSBsaXN0KCkgIyBuYW1lIHBvb2xzIHVzZWQgaW4gc2F2ZSBkYXRhOyBiZSBjYXJlZnVsIQ0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoZGlyVmlraW5nKSkgew0KICB0ZW1wIDwtIGxvYWQoZmlsZS5wYXRoKA0KICAgIGRpclZpa2luZ1tpXSwgDQogICAgcGFzdGUwKCJMYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nLVBvb2xNYXRzIiwgDQogICAgICAgICAgIGMoOSwgMTApW2ldLCANCiAgICAgICAgICAgIi5SRFMiKQ0KICApKQ0KICBtYXRzW1tpXV0gPC0gZXZhbChwYXJzZSh0ZXh0ID0gdGVtcFsxXSkpDQogIHBvb2xzYWxsW1tpXV0gPC0gZXZhbChwYXJzZSh0ZXh0ID0gdGVtcFsyXSkpDQp9DQpwb29scyA8LSBwb29sc2FsbA0KYGBgDQoNCiMjIyBBYnVuZGFuY2VzDQpgYGB7ciBsb2FkQWJ1bmRhbmNlc30NCiMgRmlyc3QsIGNoZWNrIGlmIGl0IGlzIGluIHRoZSBwYXJhbUZyYW1lLg0KIyBTZWNvbmQsIGNoZWNrIGlmIGl0IGlzIGluIHRoZSBzYXZlZCBkYXRhIGZyb20gdGhlIHByZXZpb3VzLg0KIyBPdGhlcndpc2UsIGlnbm9yZSBpdCwgd2UnbGwgZmlndXJlIG91dCB3aGF0IGl0IGlzIGFuZCB3aHkgaXQgaXMgbWlzc2luZyBsYXRlci4NCg0KY2FuZGlkYXRlRGF0YSRDb21tdW5pdHlBYnVuZCA8LSAiIg0KDQpmb3IgKHIgaW4gMTpucm93KGNhbmRpZGF0ZURhdGEpKSB7DQogICMgSUQgMTo0IGFyZSB1c2VkIHRvIGlkZW50aWZ5IHBhcmFtRnJhbWUsIDUgdXNlZCB0byBpZGVudGlmeSBhYnVuZGFuY2UNCiAgSUQgPC0gY2FuZGlkYXRlRGF0YVtyLCAxOjZdDQogIHBhcmFtRnJhbWVSb3cgPC0gcGFyYW1GcmFtZSAlPiUgZHBseXI6OmZpbHRlcigNCiAgICBDb21ibk51bSA9PSBJRCRDb21ibk51bSwNCiAgICBCYXNhbHMgPT0gSUQkQmFzYWxzLA0KICAgIENvbnN1bWVycyA9PSBJRCRDb25zdW1lcnMsDQogICAgRGF0YXNldCA9PSBJRCREYXRhc2V0DQogICkNCiAgDQogIGlmIChpcy5saXN0KHBhcmFtRnJhbWVSb3ckRW5kU3RhdGVBYnVuZGFuY2VbWzFdXSkpIHsNCiAgICBlbnRyeSA8LSB3aGljaChJRCRDb21tdW5pdGllcyA9PSBuYW1lcyhwYXJhbUZyYW1lUm93JEVuZFN0YXRlQWJ1bmRhbmNlW1sxXV0pKQ0KICAgIGlmIChsZW5ndGgoZW50cnkpKSB7DQogICAgICBjYW5kaWRhdGVEYXRhJENvbW11bml0eUFidW5kW3JdIDwtIHBhcmFtRnJhbWVSb3ckRW5kU3RhdGVBYnVuZGFuY2VbWzFdXVtbZW50cnldXQ0KICAgICAgbmV4dCgpDQogICAgfQ0KICB9DQp9DQpgYGANCg0KYGBge3IgZmlsdGVyTm9BYnVuZH0NCnByaW50KHBhc3RlKCJGYWlsdXJlczoiLCANCiAgICAgICAgICAgIHN1bShjYW5kaWRhdGVEYXRhJENvbW11bml0eUFidW5kICVpbiUgDQogICAgICAgICAgICAgICAgICBjKCIiLCAiRmFpbHVyZSIsICJFc3RpbWF0ZWRGYWlsdXJlIikpKSkNCmNhbmRpZGF0ZURhdGEgPC0gY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcihDb21tdW5pdHlBYnVuZCAhPSAiIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb21tdW5pdHlBYnVuZCAhPSAiRmFpbHVyZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29tbXVuaXR5QWJ1bmQgIT0gIkVzdGltYXRlRmFpbHVyZSIpDQpgYGANCg0KYGBge3IgY29tcHV0ZVByb2R1Y3Rpdml0eX0NCmNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5UHJvZCA8LSBOQQ0KZm9yIChyIGluIDE6bnJvdyhjYW5kaWRhdGVEYXRhKSkgew0KICBjYW5kaWRhdGVEYXRhJENvbW11bml0eVByb2Rbcl0gPC0gd2l0aCgNCiAgICBjYW5kaWRhdGVEYXRhW3IsIF0sIA0KICAgIFJNVFJDb2RlMjo6UHJvZHVjdGl2aXR5KA0KICAgICAgUG9vbCA9IHBvb2xzW1tEYXRhc2V0SURdXVtbQ29tYm5OdW1dXSwgDQogICAgICBJbnRlcmFjdGlvbk1hdHJpeCA9IG1hdHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dLCANCiAgICAgIENvbW11bml0eSA9IENvbW11bml0aWVzLCANCiAgICAgIFBvcHVsYXRpb25zID0gQ29tbXVuaXR5QWJ1bmQNCiAgICApDQogICkNCn0NCmBgYA0KDQpgYGB7ciBmaWx0ZXJOdW1lcmljYWxseVVuc3RhYmxlfQ0KcHJpbnQocGFzdGUoIk51bWVyaWNhbGx5IFVuc3RhYmxlOiIsIA0KICAgICAgICAgICAgc3VtKGNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5UHJvZCA+IDEwXjEwIA0KICAgICAgICAgICAgICAgICAgKSkpDQpjYW5kaWRhdGVEYXRhIDwtIGNhbmRpZGF0ZURhdGEgJT4lIGRwbHlyOjpmaWx0ZXIoQ29tbXVuaXR5UHJvZCA8IDEwXjEwKQ0KYGBgDQoNCiMjIENvbW11bml0aWVzDQpgYGB7ciB3aGF0cmVtYWlucywgb3V0LndpZHRoPSIxMDAlIn0NCiMgRm9yIHVzYWdlIGJ5IHRoZSByZWFkZXIuDQoNCnBsb3RTY2FsaW5nIDwtIHBsb3RseTo6cGxvdF9seSgNCiAgY2FuZGlkYXRlRGF0YSwNCiAgeCA9IH5CYXNhbHMsDQogIHkgPSB+Q29uc3VtZXJzLA0KICB6ID0gfkNvbW11bml0eVNpemUsDQogIGNvbG9yID0gfkRhdGFzZXQsDQogIGNvbG9ycyA9IGMoInJlZCIsICJibHVlIiwgImJsYWNrIikNCikNCg0KcGxvdFNjYWxpbmcgPC0gcGxvdGx5OjphZGRfbWFya2VycyhwbG90U2NhbGluZykNCg0KcGxvdFNjYWxpbmcgPC0gcGxvdGx5OjpsYXlvdXQoDQogIHBsb3RTY2FsaW5nLA0KICBzY2VuZSA9IGxpc3QoDQogICAgeGF4aXMgPSBsaXN0KHR5cGUgPSAibG9nIiksDQogICAgeWF4aXMgPSBsaXN0KHR5cGUgPSAibG9nIiksDQogICAgY2FtZXJhID0gbGlzdCgNCiAgICAgIGV5ZSA9IGxpc3QoDQogICAgICAgIHggPSAtMS4yNSwgeSA9IC0xLjI1LCB6ID0gLjA1DQogICAgICApDQogICAgKQ0KICApDQopDQoNCnBsb3RTY2FsaW5nDQpgYGANCg0KIyMgQ29tbXVuaXRpZXMgRGF0YQ0KYGBge3J9DQpjYW5kaWRhdGVEYXRhDQpgYGANCg0KIyMgRm9vZHdlYnMsIFByZXANCmBgYHtyIGNyZWF0ZUdyYXBoc30NCmZvb2RXZWJzIDwtIGxpc3QoKQ0KDQpmb3IgKHIgaW4gMTpucm93KGNhbmRpZGF0ZURhdGEpKSB7DQogIGZvb2RXZWJzW1tyXV0gPC0gd2l0aCgNCiAgICBjYW5kaWRhdGVEYXRhW3IsIF0sDQogICAgew0KICAgICAgcmVkQ29tIDwtIFJNVFJDb2RlMjo6Q3N2Um93U3BsaXQoQ29tbXVuaXRpZXMpDQogICAgICByZWRNYXQgPC0gbWF0c1tbRGF0YXNldElEXV1bW0NvbWJuTnVtXV1bcmVkQ29tLCByZWRDb21dDQogICAgICByZWRQb29sIDwtIHBvb2xzW1tEYXRhc2V0SURdXVtbQ29tYm5OdW1dXVtyZWRDb20sIF0NCiAgICAgIA0KICAgICAgY29sbmFtZXMocmVkTWF0KSA8LSBwYXN0ZTAoJ3MnLGFzLmNoYXJhY3RlcihyZWRDb20pKQ0KICAgICAgcm93bmFtZXMocmVkTWF0KSA8LSBjb2xuYW1lcyhyZWRNYXQpDQogICAgICANCiAgICAgIG5hbWVzKHJlZFBvb2wpWzFdIDwtICJub2RlIg0KICAgICAgcmVkUG9vbCRub2RlIDwtIGNvbG5hbWVzKHJlZE1hdCkNCiAgICAgIG5hbWVzKHJlZFBvb2wpWzNdIDwtICJNIg0KICAgICAgDQogICAgICBHcmFwaCA8LSBpZ3JhcGg6OmdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeCgNCiAgICAgICAgcmVkTWF0LCB3ZWlnaHRlZCA9IFRSVUUNCiAgICAgICkNCiAgICAgIA0KICAgICAgR3JhcGggPC0gaWdyYXBoOjpzZXQudmVydGV4LmF0dHJpYnV0ZSgNCiAgICAgICAgR3JhcGgsICJuYW1lIiwgdmFsdWUgPSBjb2xuYW1lcyhyZWRNYXQpDQogICAgICApDQogICAgICANCiAgICAgIHJlZFBvb2wkTiA8LSBSTVRSQ29kZTI6OkNzdlJvd1NwbGl0KENvbW11bml0eUFidW5kKQ0KICAgICAgDQogICAgICAjIEZvciBsYXRlciBhbmFseXNpcywgdGFrZSB0aGUgbWF0cml4IGRpYWdvbmFsLg0KICAgICAgDQogICAgICByZWRQb29sJEludHJhc3BlY2lmaWMgPC0gZGlhZyhyZWRNYXQpDQogICAgICANCiAgICAgIEdyYXBoQXNEYXRhRnJhbWUgPC0gaWdyYXBoOjphc19kYXRhX2ZyYW1lKEdyYXBoKQ0KICANCiAgICAgICMgQWRkIGluIGFidW5kYW5jZXMgZm9yIGNhbGN1bGF0aW5nIGFidW5kYW5jZSAqIChnYWluIG9yIGxvc3MpDQogICAgICBHcmFwaEFzRGF0YUZyYW1lIDwtIGRwbHlyOjpsZWZ0X2pvaW4oDQogICAgICAgIEdyYXBoQXNEYXRhRnJhbWUsDQogICAgICAgIGRwbHlyOjpzZWxlY3QocmVkUG9vbCwgbm9kZSwgTiksDQogICAgICAgIGJ5ID0gYygidG8iID0gIm5vZGUiKQ0KICAgICAgKQ0KICANCiAgICAgICMgU3BsaXQgZGF0YSBmcmFtZS4NCiAgICAgIFJlc0NvbiA8LSBHcmFwaEFzRGF0YUZyYW1lW0dyYXBoQXNEYXRhRnJhbWUkd2VpZ2h0ID4gMCxdDQogICAgICBDb25SZXMgPC0gR3JhcGhBc0RhdGFGcmFtZVtHcmFwaEFzRGF0YUZyYW1lJHdlaWdodCA8IDAsXQ0KICAgICAgDQogICAgICAjIFJlb3JkZXIgYW5kIHJlbmFtZSB2YXJpYWJsZXMuDQogICAgICBSZXNDb24gPC0gZHBseXI6OnNlbGVjdChSZXNDb24sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8sIGZyb20sICMgcmVzb3VyY2UgPSB0bywgY29uc3VtZXIgPSBmcm9tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdFBlclVuaXQgPSB3ZWlnaHQsIHJlc291cmNlQWJ1bmQgPSBOKQ0KICAgICAgQ29uUmVzIDwtIGRwbHlyOjpzZWxlY3QoQ29uUmVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvLCBmcm9tLCAjIHJlc291cmNlID0gZnJvbSwgY29uc3VtZXIgPSB0bywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZmZlY3RQZXJVbml0ID0gd2VpZ2h0LCBjb25zdW1lckFidW5kID0gTikNCiAgICAgIFJlc0NvbiA8LSBkcGx5cjo6bXV0YXRlKGRwbHlyOjpncm91cF9ieShSZXNDb24sIGZyb20pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWZmZWN0QWN0dWFsID0gZWZmZWN0UGVyVW5pdCAqIHJlc291cmNlQWJ1bmQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUeXBlID0gIkV4cGxvaXQrIikNCiAgICAgIENvblJlcyA8LSBkcGx5cjo6bXV0YXRlKGRwbHlyOjpncm91cF9ieShDb25SZXMsIGZyb20pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWZmZWN0QWN0dWFsID0gZWZmZWN0UGVyVW5pdCAqIGNvbnN1bWVyQWJ1bmQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUeXBlID0gaWZlbHNlKGZyb20gPT0gdG8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTZWxmUmVnLSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFeHBsb2l0LSIpKQ0KICAgICAgDQogICAgICBJbnRyaUcgPC0gd2l0aChyZWRQb29sLCBkYXRhLmZyYW1lKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbSA9IG5vZGUsICNyZXNvdXJjZSA9IG5vZGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0byA9IG5vZGUsICNjb25zdW1lciA9IG5vZGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZmZlY3RQZXJVbml0ID0gaWZlbHNlKFJlcHJvZHVjdGlvblJhdGUgPiAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVwcm9kdWN0aW9uUmF0ZSwgMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZmZlY3RBY3R1YWwgPSBpZmVsc2UoUmVwcm9kdWN0aW9uUmF0ZSA+IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE4gKiBSZXByb2R1Y3Rpb25SYXRlLCAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFR5cGUgPSAiSW50cmlzYysiKSkgDQogICAgICBJbnRyaUwgPC0gd2l0aChyZWRQb29sLCBkYXRhLmZyYW1lKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbSA9IG5vZGUsICNyZXNvdXJjZSA9IG5vZGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0byA9IG5vZGUsICNjb25zdW1lciA9IG5vZGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZmZlY3RQZXJVbml0ID0gaWZlbHNlKFJlcHJvZHVjdGlvblJhdGUgPCAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVwcm9kdWN0aW9uUmF0ZSwgMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZmZlY3RBY3R1YWwgPSBpZmVsc2UoUmVwcm9kdWN0aW9uUmF0ZSA8IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE4gKiBSZXByb2R1Y3Rpb25SYXRlLCAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFR5cGUgPSAiSW50cmlzYy0iKSkNCiAgICAgIA0KICAgICAgRWRnZURhdGFGcmFtZSA8LSBkcGx5cjo6YmluZF9yb3dzKA0KICAgICAgICBkcGx5cjo6c2VsZWN0KFJlc0NvbiwgLXJlc291cmNlQWJ1bmQpLCANCiAgICAgICAgZHBseXI6OnNlbGVjdChDb25SZXMsIC1jb25zdW1lckFidW5kKSwNCiAgICAgICAgSW50cmlHLCBJbnRyaUwNCiAgICAgICkNCiAgICAgIA0KICAgICAgRWRnZURhdGFGcmFtZSA8LSBFZGdlRGF0YUZyYW1lICU+JSBkcGx5cjo6cmVuYW1lKA0KICAgICAgICAjIEVtcGlyaWNhbGx5IHNwZWFraW5nLCB0byBhbmQgZnJvbSBhcHBlYXIgcmV2ZXJzZWQuDQogICAgICAgICMgQSBjb25zdW1lciAoZnJvbSkgc2hvdWxkIGhhdmUgYSBuZWdhdGl2ZSBlZmZlY3Qgb24gcmVzb3VyY2UgKHRvKSwNCiAgICAgICAgIyBidXQgdGhlIG9yZ2FuaXNhdGlvbiBzbyBmYXIgbWFya3MgaXQgYXMgcG9zaXRpdmUuIFdlIGZpeCB0aGlzLg0KICAgICAgICB0ZW1wbmFtZSA9IHRvLA0KICAgICAgICB0byA9IGZyb20NCiAgICAgICkgJT4lIGRwbHlyOjpyZW5hbWUoDQogICAgICAgIGZyb20gPSB0ZW1wbmFtZQ0KICAgICAgKSAlPiUgZHBseXI6OmZpbHRlcigNCiAgICAgICAgIyBSZW1vdmUgcGxhY2Vob2xkZXIgZW50cmllcw0KICAgICAgICBlZmZlY3RQZXJVbml0ICE9IDANCiAgICAgICkgJT4lIGRwbHlyOjptdXRhdGUoDQogICAgICAgICMgVXNlZnVsIHRvIGtlZXAgZWZmZWN0cyBzZXBhcmF0ZQ0KICAgICAgICBlZmZlY3RTaWduID0gc2lnbihlZmZlY3RQZXJVbml0KQ0KICAgICAgKSAlPiUgZ3JvdXBfYnkoDQogICAgICAgIHRvLCBlZmZlY3RTaWduDQogICAgICApICU+JSBkcGx5cjo6bXV0YXRlKA0KICAgICAgICAjIFBlcmZvcm0gdGhlIHBvc3QgbW9ydGVtIG9mIHRoZSBtb3N0IGluZmx1ZW50aWFsIGZyb20ncw0KICAgICAgICBlZmZlY3RFZmZpY2llbmN5ID0gZWZmZWN0UGVyVW5pdCAvIHN1bShlZmZlY3RQZXJVbml0KSwgDQogICAgICAgIGVmZmVjdE5vcm1hbGlzZWQgPSBlZmZlY3RBY3R1YWwgLyBzdW0oZWZmZWN0QWN0dWFsKQ0KICAgICAgKSAlPiUgZHBseXI6OmFycmFuZ2UodG8pDQogICAgICANCiAgICAgIGxpc3QoDQogICAgICAgIEVkZ2VzID0gRWRnZURhdGFGcmFtZSwNCiAgICAgICAgVmVydGljZXMgPSByZWRQb29sDQogICAgICApDQogICAgfQ0KICApDQp9DQpgYGANCg0KYGBge3IgZnVuY3Rpb25zfQ0KdG9DaGVkZGFyIDwtIGZ1bmN0aW9uKEVWTGlzdCwgbmFtZSA9ICIiKSB7IyBFZGdlcyBWZXJ0aWNlcyBMaXN0DQogIGxpbmtzIDwtIEVWTGlzdCRFZGdlcw0KDQogICMgY2hlZGRhciBkb2VzIG5vdCBsaWtlICJjYW5uaWJhbGlzbSIuDQogIGxpbmtzIDwtIGxpbmtzWw0KICAgIGxpbmtzJHRvICE9IGxpbmtzJGZyb20sDQogIF0NCg0KICAjICJbQ11vbHVtbnMgY2FsbGVkIOKAmHJlc291cmNl4oCZIGFuZCDigJhjb25zdW1lcuKAmSBtdXN0IGJlIGdpdmVuLiINCiAgbGlua3MgPC0gZHBseXI6OmJpbmRfcm93cygNCiAgICBsaW5rcyAlPiUgZHBseXI6OmZpbHRlcihlZmZlY3RTaWduID09IDEpICU+JSBkcGx5cjo6cmVuYW1lKA0KICAgICAgcmVzb3VyY2UgPSBmcm9tLCBjb25zdW1lciA9IHRvKSwNCiAgICBsaW5rcyAlPiUgZHBseXI6OmZpbHRlcihlZmZlY3RTaWduID09IC0xKSAlPiUgZHBseXI6OnJlbmFtZSgNCiAgICAgIHJlc291cmNlID0gdG8sIGNvbnN1bWVyID0gZnJvbSksDQogICkgJT4lIGRwbHlyOjpzZWxlY3QoLVR5cGUpICMgQ2hlZGRhciBjb25mdXNlcyBub2RlIFR5cGUgYW5kIGVkZ2UgVHlwZS4NCg0KICBjaGVkZGFyOjpDb21tdW5pdHkoDQogICAgbm9kZXMgPSBFVkxpc3QkVmVydGljZXMsDQogICAgcHJvcGVydGllcyA9IGxpc3QoDQogICAgICB0aXRsZSA9IG5hbWUsDQogICAgICBNLnVuaXRzID0gIm1hc3NlcyIsDQogICAgICBOLnVuaXRzID0gImFidW5kIg0KICAgICksDQogICAgdHJvcGhpYy5saW5rcyA9IGxpbmtzDQogICkNCn0NCg0KdG9JR3JhcGggPC0gZnVuY3Rpb24oRVZMaXN0LCBzaWduID0gMCkgew0KICBpZ3JhcGg6OmdyYXBoX2Zyb21fZGF0YV9mcmFtZSgNCiAgICBkID0gaWYoc2lnbiA9PSAwKSB7DQogICAgICBFVkxpc3QkRWRnZXMNCiAgICB9IGVsc2Ugew0KICAgICAgRVZMaXN0JEVkZ2VzW0VWTGlzdCRFZGdlcyRlZmZlY3RTaWduID09IHNpZ24sIF0NCiAgICB9LA0KICAgIGRpcmVjdGVkID0gVFJVRSwNCiAgICB2ZXJ0aWNlcyA9IEVWTGlzdCRWZXJ0aWNlcw0KICApDQp9DQoNCg0KdG9Qb3N0TW9ydGVtIDwtIGZ1bmN0aW9uKEVWTGlzdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICB0aHJlc2hvbGQgPSAwLCAjIHNldHMgdG8gbWluaW1hbCBzaXplIGVkZ2VzIGJlbG93DQogICAgICAgICAgICAgICAgICAgICAgICAgbm9kZVNpemUgPSBjKCJOb25lIiwgIkFidW5kYW5jZSIsICJTaXplIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgZWRnZVNjYWxlID0gMTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWNlZFRyb3BoaWMgPSBUUlVFKSB7DQogIGlmICh0b2xvd2VyKHRocmVzaG9sZCkgPT0gImFkYXB0aXZlIikgew0KICAgIHRocmVzaG9sZCA9IEVWTGlzdCRFZGdlcyAlPiUgZ3JvdXBfYnkoDQogICAgICB0bywgZWZmZWN0U2lnbg0KICAgICkgJT4lIHN1bW1hcmlzZSgNCiAgICAgIG1heCA9IG1heChlZmZlY3ROb3JtYWxpc2VkKSwgLmdyb3VwcyA9ICJkcm9wIg0KICAgICkgJT4lIHVuZ3JvdXAgJT4lIHB1bGwobWF4KSAlPiUgbWluDQogIH0NCg0KICB0aGVHYyA8LSB0b0NoZWRkYXIoRVZMaXN0LCBuYW1lID0gIlRyb3BoaWMgTGV2ZWxzIikNCiAgdGhlR2kgPC0gdG9JR3JhcGgoRVZMaXN0KQ0KDQogIHRoZUdpR2FpbiA8LSB0b0lHcmFwaChFVkxpc3QsIHNpZ24gPSAxKQ0KICB0aGVHaUxvc3MgPC0gdG9JR3JhcGgoRVZMaXN0LCBzaWduID0gLTEpDQoNCiAgdGhlTGF5b3V0IDwtIGlncmFwaDo6bGF5b3V0LmNpcmNsZSh0aGVHaSkNCg0KICB0aGVTaXplIDwtIG1hdGNoLmFyZyhub2RlU2l6ZSwgYygiQWJ1bmRhbmNlIiwgIlNpemUiLCAiTm9uZSIpKQ0KICBpZiAodGhlU2l6ZSA9PSAiQWJ1bmRhbmNlIikNCiAgICB0aGVWcyA8LSBzcXJ0KGlncmFwaDo6dmVydGV4X2F0dHIodGhlR2kpJE4pICogMTANCiAgZWxzZSBpZiAodGhlU2l6ZSA9PSAiU2l6ZSIpIHsNCiAgICB0aGVWcyA8LSBpZ3JhcGg6OnZlcnRleF9hdHRyKHRoZUdpKSRNDQogICAgdGhlVnMgPC0gc3FydCh0aGVWcyAvIG1pbih0aGVWcykpICogMTANCiAgfSBlbHNlIGlmICh0aGVTaXplID09ICJOb25lIikgew0KICAgIHRoZVZzIDwtIDE1DQogIH0NCg0KICB0aGVDb2xvcnMgPC0gaWZlbHNlKA0KICAgIGlncmFwaDo6dmVydGV4X2F0dHIodGhlR2kpJFR5cGUgPT0gIkJhc2FsIiwgInNreWJsdWUiLCAicmVkIg0KICApDQogIGlmICgiQ29yZSIgJWluJSBuYW1lcyhpZ3JhcGg6OnZlcnRleF9hdHRyKHRoZUdpKSkpIHsNCiAgICB0aGVTaGFwZXMgPC0gaWZlbHNlKGlncmFwaDo6dmVydGV4X2F0dHIodGhlR2kpJENvcmUsDQogICAgICAgICAgICAgICAgICAgICAgICAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgMSkNCiAgfSBlbHNlIHsNCiAgICB0aGVTaGFwZXMgPC0gMSANCiAgICAjIE5vdGUgSWdyYXBoIHVzZXMgImNpcmNsZSIgdGhlbiAicmVjdGFuZ2xlIiwNCiAgICAjIGJ1dCBSIGFuZCBjaGVkZGFyIHVzZSAicmVjdGFuZ2xlIiB0aGVuICJjaXJjbGUiLCBzbyB3ZSB3aWxsIHVzZSBhICEuDQogIH0NCg0KICB0aGVCb3RoIDwtIGlncmFwaDo6ZWRnZV9hdHRyKHRoZUdpKSRlZmZlY3ROb3JtYWxpc2VkDQogIHRoZUdhaW4gPC0gaWdyYXBoOjplZGdlX2F0dHIodGhlR2lHYWluKSRlZmZlY3ROb3JtYWxpc2VkDQogIHRoZUxvc3MgPC0gaWdyYXBoOjplZGdlX2F0dHIodGhlR2lMb3NzKSRlZmZlY3ROb3JtYWxpc2VkDQoNCiAgdGhlQm90aFt0aGVCb3RoIDwgdGhyZXNob2xkXSA8LSAwDQogIHRoZUdhaW5bdGhlR2FpbiA8IHRocmVzaG9sZF0gPC0gMA0KICB0aGVMb3NzW3RoZUxvc3MgPCB0aHJlc2hvbGRdIDwtIDANCg0KICAjIEluZm9ybSB0aGUgZ3JhcGhzIG9mIHdoaWNoIGVkZ2VzIGFyZSBub3QgbmVlZGVkLg0KICB0aGVHaSA8LSBpZ3JhcGg6OmRlbGV0ZV9lZGdlcyh0aGVHaSwgd2hpY2godGhlQm90aCA9PSAwKSkNCiAgdGhlR2lHYWluIDwtIGlncmFwaDo6ZGVsZXRlX2VkZ2VzKHRoZUdpR2Fpbiwgd2hpY2godGhlR2FpbiA9PSAwKSkNCiAgdGhlR2lMb3NzIDwtIGlncmFwaDo6ZGVsZXRlX2VkZ2VzKHRoZUdpTG9zcywgd2hpY2godGhlTG9zcyA9PSAwKSkNCg0KICAjIFJlbW92ZSB0aGUgc2FtZSBlbnRyaWVzIHNvIHRoYXQgbGVuZ3RocyBtYXRjaC4NCiAgdGhlR2FpbiA8LSB0aGVHYWluW3RoZUdhaW4gPiAwXQ0KICB0aGVMb3NzIDwtIHRoZUxvc3NbdGhlTG9zcyA+IDBdDQoNCiAgdGhlR2FpbiA8LSB0aGVHYWluICogZWRnZVNjYWxlDQogIHRoZUxvc3MgPC0gdGhlTG9zcyAqIGVkZ2VTY2FsZQ0KDQogIHBhcm9sZCA8LSBwYXIobm8ucmVhZG9ubHkgPSBUUlVFKQ0KICBwYXIobWZyb3cgPSBjKDIsIDIpLCAjIFR3byBSb3dzLCBUd28gQ29sdW1ucw0KICAgICAgbWFyID0gYygwLCAxLjUsIDEsIDApLCAjIE1hcmdpbnMsIGJvdHRvbSwgbGVmdCwgdG9wLCByaWdodA0KICAgICAgb21hID0gYygwLjEsIDAuMSwgMC4xLCAwLjEpICMgT3V0ZXIgbWFyZ2lucy4NCiAgKQ0KDQogIGNoZWRkYXI6OlBsb3RXZWJCeUxldmVsKA0KICAgIHRoZUdjLA0KICAgIHNob3cubGV2ZWwubGluZXMgPSBUUlVFLA0KICAgICMgSGFkIGJlZW4gdXNpbmcgTG9uZ1dlaWdodGVkLCBidXQgdGhhdCBzZWVtcyB0byBnaXZlIHRoZSB1cHNpZGUgZG93biBULg0KICAgICMgRmxvdyBiYXNlZCBzZWVtcyB0byBiZSBtb3JlIHdoYXQgd2UgYXJlIGV4cGVjdGluZywgZ2l2ZW4gdGhlIHVzYWdlIG9mDQogICAgIyB0aHJlc2hvbGRpbmcgYW5kIHdoYXQgdGhhdCBzaG93cy4gVGhlIGZsb3dzIGhlcmUgYXJlIGV4cGVjdGVkIHRvIGJlDQogICAgIyBmbG93cyBvZiBlbmVyZ3kgdGhyb3VnaCB0aGUgZm9vZCB3ZWIuDQogICAgbGV2ZWwgPSBjaGVkZGFyOjpGbG93QmFzZWRUcm9waGljTGV2ZWwodGhlR2MsIHdlaWdodC5ieSA9ICJlZmZlY3ROb3JtYWxpc2VkIiksDQogICAgICBjb2wgPSB0aGVDb2xvcnMsDQogICAgICBwY2ggPSB0aGVTaGFwZXMNCiAgKQ0KDQogIGlmICghcmVkdWNlZFRyb3BoaWMpIHsNCiAgICBwbG90KA0KICAgICAgdGhlR2ksDQogICAgICBsYXlvdXQgPSB0aGVMYXlvdXQsDQogICAgICB2ZXJ0ZXguc2l6ZSA9IHRoZVZzLA0KICAgICAgZWRnZS53aWR0aCA9IDEsDQogICAgICBlZGdlLmFycm93LnNpemUgPSAwLjMsDQogICAgICBlZGdlLmFycm93LndpZHRoID0gMSwNCiAgICAgIHZlcnRleC5jb2xvciA9IHRoZUNvbG9ycywNCiAgICAgIHZlcnRleC5zaGFwZSA9IGlncmFwaDo6c2hhcGVzKClbYXMubnVtZXJpYyghdGhlU2hhcGVzKSArIDFdLA0KICAgICAgZWRnZS5sdHkgPSAyLA0KICAgICAgZWRnZS5jb2xvciA9ICJncmV5IiwNCiAgICAgIGVkZ2UuYXJyb3cubW9kZSA9ICI+IiwNCiAgICAgIG1haW4gPSAiQ29uc3VtcHRpb24iDQogICAgKQ0KICB9IGVsc2Ugew0KICAgIEVWTGlzdFJlZCA8LSBFVkxpc3QNCiAgICBFVkxpc3RSZWQkRWRnZXMgPC0gRVZMaXN0UmVkJEVkZ2VzICU+JSBkcGx5cjo6ZmlsdGVyKA0KICAgICAgZWZmZWN0Tm9ybWFsaXNlZCA+PSB0aHJlc2hvbGQNCiAgICApDQogICAgdGhlR2MyIDwtIHRvQ2hlZGRhcihFVkxpc3RSZWQsIG5hbWUgPSAiU3Ryb25nZXN0IFRyb3BoaWMgTGV2ZWxzIikNCiAgICBjaGVkZGFyOjpQbG90V2ViQnlMZXZlbCgNCiAgICAgIHRoZUdjMiwNCiAgICAgIHNob3cubGV2ZWwubGluZXMgPSBUUlVFLA0KICAgIGxldmVsID0gY2hlZGRhcjo6Rmxvd0Jhc2VkVHJvcGhpY0xldmVsKHRoZUdjMiwgd2VpZ2h0LmJ5ID0gImVmZmVjdE5vcm1hbGlzZWQiKSwNCiAgICAgIGNvbCA9IHRoZUNvbG9ycywNCiAgICAgIHBjaCA9IHRoZVNoYXBlcw0KICAgICkNCiAgfQ0KDQogIHBsb3QoDQogICAgdGhlR2lHYWluLA0KICAgIGxheW91dCA9IHRoZUxheW91dCwNCiAgICB2ZXJ0ZXguc2l6ZSA9IHRoZVZzLA0KICAgIGVkZ2Uud2lkdGggPSB0aGVHYWluLA0KICAgIGVkZ2UuYXJyb3cuc2l6ZSA9IDAuMywNCiAgICBlZGdlLmFycm93LndpZHRoID0gMSwNCiAgICB2ZXJ0ZXguY29sb3IgPSB0aGVDb2xvcnMsDQogICAgICB2ZXJ0ZXguc2hhcGUgPSBpZ3JhcGg6OnNoYXBlcygpW2FzLm51bWVyaWMoIXRoZVNoYXBlcykgKyAxXSwNCiAgICBlZGdlLmx0eSA9IDIsDQogICAgZWRnZS5jb2xvciA9ICJibHVlIiwNCiAgICBlZGdlLmFycm93Lm1vZGUgPSAiPiIsDQogICAgbWFpbiA9ICJDb25zdW1lcidzIEdhaW5zIg0KICApDQoNCiAgcGxvdCgNCiAgICB0aGVHaUxvc3MsDQogICAgbGF5b3V0ID0gdGhlTGF5b3V0LA0KICAgIHZlcnRleC5zaXplID0gdGhlVnMsDQogICAgZWRnZS53aWR0aCA9IHRoZUxvc3MsDQogICAgZWRnZS5hcnJvdy5zaXplID0gMC4zLA0KICAgIGVkZ2UuYXJyb3cud2lkdGggPSAyLA0KICAgIHZlcnRleC5jb2xvciA9IHRoZUNvbG9ycywNCiAgICAgIHZlcnRleC5zaGFwZSA9IGlncmFwaDo6c2hhcGVzKClbYXMubnVtZXJpYyghdGhlU2hhcGVzKSArIDFdLA0KICAgIGVkZ2UubHR5ID0gMywNCiAgICBlZGdlLmNvbG9yID0gImRhcmtyZWQiLA0KICAgIGVkZ2UuYXJyb3cubW9kZSA9ICI8IiwNCiAgICBtYWluID0gIlJlc291cmNlJ3MgTG9zc2VzIg0KICApDQogIA0KICBwYXIocGFyb2xkKQ0KICANCiAgRVZMaXN0JEVkZ2VzICU+JSBkcGx5cjo6dW5ncm91cCgpICU+JSBkcGx5cjo6ZmlsdGVyKA0KICAgIGVmZmVjdE5vcm1hbGlzZWQgPj0gdGhyZXNob2xkDQogICkgJT4lIGRwbHlyOjpzZWxlY3QoDQogICAgLWVmZmVjdFNpZ24NCiAgKSAlPiUgZHBseXI6OmFycmFuZ2UoDQogICAgdG8sIC1lZmZlY3ROb3JtYWxpc2VkDQogICkNCn0NCg0KYGBgDQoNCg0KIyMgRm9vZHdlYnMsIE92ZXJsYXBwaW5nIHsudGFic2V0fQ0KIyMjIDENCmBgYHtyIGdhbGxlcnkxfQ0KaSA8LSAxDQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDINCmBgYHtyIGdhbGxlcnkyfQ0KaSA8LSAyDQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDMNCmBgYHtyIGdhbGxlcnkzfQ0KaSA8LSAzDQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDQNCmBgYHtyIGdhbGxlcnk0fQ0KaSA8LSA0DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDUNCmBgYHtyIGdhbGxlcnk1fQ0KaSA8LSA1DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDYNCmBgYHtyIGdhbGxlcnk2fQ0KaSA8LSA2DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDcNCmBgYHtyIGdhbGxlcnk3fQ0KaSA8LSA3DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDgNCmBgYHtyIGdhbGxlcnk4fQ0KaSA8LSA4DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCiMjIEZvb2R3ZWJzLCBMYXJnZXIgRm9vZCBQcmVmZXJlbmNlIHsudGFic2V0fQ0KIyMjIDkNCmBgYHtyIGdhbGxlcnk5fQ0KaSA8LSA5DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDEwDQpgYGB7ciBnYWxsZXJ5MTB9DQppIDwtIDEwDQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDExDQpgYGB7ciBnYWxsZXJ5MTF9DQppIDwtIDExDQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDEyDQpgYGB7ciBnYWxsZXJ5MTJ9DQppIDwtIDEyDQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDEzDQpgYGB7ciBnYWxsZXJ5MTN9DQppIDwtIDEzDQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDE0DQpgYGB7ciBnYWxsZXJ5MTR9DQppIDwtIDE0DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDE1DQpgYGB7ciBnYWxsZXJ5MTV9DQppIDwtIDE1DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDE2DQpgYGB7ciBnYWxsZXJ5MTZ9DQppIDwtIDE2DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDE3DQpgYGB7ciBnYWxsZXJ5MTd9DQppIDwtIDE3DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDE4DQpgYGB7ciBnYWxsZXJ5MTh9DQppIDwtIDE4DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg0KIyMjIDE5DQpgYGB7ciBnYWxsZXJ5MTl9DQppIDwtIDE5DQp0b1Bvc3RNb3J0ZW0oZm9vZFdlYnNbW2ldXSwgbm9kZVNpemUgPSAiTm9uZSIsIHRocmVzaG9sZCA9ICJBZGFwdGl2ZSIpIC0+IHRlbXANCmBgYA0KYGBge3J9DQp0ZW1wDQpgYGANCg==